package hu.mapro.gwtui.gxt.client;

import hu.mapro.gwtui.client.Action;
import hu.mapro.gwtui.client.Visibility;
import hu.mapro.gwtui.client.widget.Command;
import hu.mapro.model.Getter;
import hu.mapro.model.ModelUtil;
import hu.mapro.model.Setter;
import hu.mapro.model.meta.HasLabel;
import hu.mapro.model.meta.HasPath;

import java.util.Map;

import com.google.common.base.Function;
import com.google.common.collect.Maps;
import com.google.gwt.event.shared.HandlerRegistration;
import com.sencha.gxt.core.client.ValueProvider;
import com.sencha.gxt.widget.core.client.button.TextButton;
import com.sencha.gxt.widget.core.client.event.HideEvent;
import com.sencha.gxt.widget.core.client.event.HideEvent.HideHandler;
import com.sencha.gxt.widget.core.client.event.SelectEvent;
import com.sencha.gxt.widget.core.client.event.SelectEvent.SelectHandler;
import com.sencha.gxt.widget.core.client.event.ShowEvent;
import com.sencha.gxt.widget.core.client.event.ShowEvent.ShowHandler;
import com.sencha.gxt.widget.core.client.grid.ColumnConfig;

public class GxtUiUtils {

	public static class ButtonCommand implements Command {
		private final TextButton button;

		public ButtonCommand(TextButton button) {
			this.button = button;
		}

		@Override
		public void setVisible(boolean visible) {
			button.setVisible(visible);
		}

		@Override
		public void setLabel(String label) {
			button.setText(label);
		}

		@Override
		public void setEnabled(boolean enabled) {
			button.setEnabled(enabled);
		}

		@Override
		public void setAction(final Action action) {
			button.addSelectHandler(new SelectHandler() {
				@Override
				public void onSelect(SelectEvent event) {
					action.perform();
				}
			});
		}
	}

	public static final Command createCommand(final TextButton button) {
		return new ButtonCommand(button);		
	}
	
	static class VisReg {

		protected HandlerRegistration hide;
		protected HandlerRegistration show;
		
	}
	
	public static Visibility wrap(final com.sencha.gxt.widget.core.client.Component component) {

		return new Visibility() {
			
			Map<Action, VisReg> map = Maps.newHashMap();
			
			@Override
			public void removeChangeHandler(Action action) {
				VisReg reg = map.get(action);
				reg.show.removeHandler();
				reg.hide.removeHandler();
			}
			
			@Override
			public boolean isVisible() {
				return component.isVisible();
			}
			
			@Override
			public void addChangeHandler(final Action action) {
				VisReg reg = new VisReg();
				reg.hide = component.addHideHandler(new HideHandler() {
					@Override
					public void onHide(HideEvent event) {
						action.perform();
					}
				});
				reg.show = component.addShowHandler(new ShowHandler() {
					
					@Override
					public void onShow(ShowEvent event) {
						action.perform();
					}
				});
				map.put(action, reg);
			}
		};
		
	}
	

	public static <T, F extends hu.mapro.model.meta.Field<T,V>&HasLabel&Getter<T, V>, V> ColumnConfig<T, V> columnConfigField(final F field) {
		return columnConfig(hasPath(field), field.getLabel(), field);
	}
	
	public static <T, V> ColumnConfig<T, V> columnConfig(HasPath path, String label, final Getter<T, V> getter) {
		return columnConfig(path, label, getter, GxtUiUtils.<T,V>fakeSetter());
	}
	
	public static <T, V> ColumnConfig<T, V> columnConfig(String label, final Getter<T, V> getter) {
		return columnConfig(FAKE_PATH, label, getter, GxtUiUtils.<T,V>fakeSetter());
	}

	public static <T, V> ColumnConfig<T, V> columnConfig(String label, final Function<T, V> function) {
		return columnConfig(label, ModelUtil.getter(function));
	}
	
	public static <T, V> ColumnConfig<T, V> columnConfig(final HasPath path, String label, final Getter<T, V> getter, final Setter<T, V> setter) {
		ColumnConfig<T, V> columnConfig = new ColumnConfig<T, V>(new ValueProvider<T, V>() {

			@Override
			public V getValue(T object) {
				return getter.get(object);
			}

			@Override
			public void setValue(T object, V value) {
				setter.set(object, value);
			}

			@Override
			public String getPath() {
				return path.getPath();
			}
		});
		columnConfig.setHeader(label);
		return columnConfig;
	}

	private static final HasPath FAKE_PATH = new HasPath() {
		@Override
		public String getPath() {
			throw new RuntimeException("getPath not implemented");
		}
	}; 

	@SuppressWarnings("rawtypes")
	private static final Setter FAKE_SETTER = new Setter() {
		@Override
		public void set(Object object, Object value) {
			throw new RuntimeException("set not implemented");
		}
	}; 
	
	@SuppressWarnings("unchecked")
	private static final <T, V> Setter<T, V> fakeSetter() {
		return FAKE_SETTER;
	}

	public static HasPath hasPath(final hu.mapro.model.meta.Field<?,?> field) {
		return new HasPath() {
			@Override
			public String getPath() {
				return field.getName();
			}
		};
	}
	
}
